<?php
/**
* @package mosRest
* @author Chad Auld and Ozgur Cem Sen (code@brilaps.com)
* @copyright Brilaps, LLC (http://brilaps.com)
* @link http://brilaps.com || http://wiki.brilaps.com
* @license http://www.opensource.org/licenses/gpl-license.php GNU/GPL v.2.
*/

/**
* The workhorse class of the com_content REST interface.  This class extends on the base
* mos_rest class which brings RESTful functionality to the MiaCMS.  The mos_rest base
* class can be used to RESTify many different components.  Use your imagination and this
* component as your example :)
*/
class mRestContent extends mRest {
	
	/**
	* Retrieves a specific content item 
	* @param contentid - id of specific content item to return
	* @param returnFormat - return format requested (ex) json, xml, php
	*/
	function getContentById($contentid, $returnFormat) {
		global $database;
		
		//Setup content query header
		$query = $this->getContentQueryHeader();
		$query .= "\n WHERE a.id={$contentid} AND a.access=0";
		$database->setQuery($query);
		$result = $database->loadObjectList();
		$data = $this->formatContentRequest($result, $returnFormat);
		return $data;
	}
	
	/**
	* Retrieves a set of content items
	* @param sectionId - allows grabbing of items for a single section (optional)
	* @param contentLimit - the number of items to return (max is 100)
	* @param contentLimitOffset - used to control the starting point of the query (ex) pagination, get more query, etc
	* @param contentCreateDateSort - used to control the sort by create date (default DESC)
	* @param returnFormat - return format requested (ex) json, xml, php
	* @param callback - the requested JSON callback function name
	*/
	function getContent($sectionId, $categoryId, $contentLimit, $contentLimitOffset, $contentCreateDateSort, $returnFormat, $callback) {
		global $database;
		
		$query = $this->getContentQueryHeader();  //Get content header
		$query .= $this->getPublishFilter(); //Append published filter
		//Add section and/or category filters if requested
		if ($sectionId>=0) {
			$query .= "\n AND sectionid={$sectionId}";
		}
		if ($categoryId>=0) {
			$query .= "\n AND catid={$categoryId}";
		}
		//Add limit, offset, and order by clause
		$query .= "\n ORDER BY created {$contentCreateDateSort}"
				. "\n LIMIT {$contentLimitOffset}, {$contentLimit}";
		$database->setQuery($query);
		$results = $database->loadObjectList();
		$data = $this->formatContentRequest($results, $returnFormat, $callback);
		return $data;
	}
	
	/**
	* Retrieves a set of content items based on their end-user rating
	* @param contentLimit - the number of items to return (max is 100)
	* @param contentLimitOffset - used to control the starting point of the query (ex) pagination, get more query, etc
	* @param returnFormat - return format requested (ex) json, xml, php
	* @param callback - the requested JSON callback function name
	*/
	function getContentByRank($contentLimit, $contentLimitOffset, $returnFormat, $callback) {
		global $database;
		
		$query = $this->getContentQueryHeader();  //Get content header
		$query .= $this->getPublishFilter(); //Append published filter
		//Add limit, offset, and order by clause
		$query .= "\n ORDER BY ROUND(v.rating_sum / v.rating_count) DESC"
				. "\n LIMIT $contentLimitOffset, $contentLimit";
		$database->setQuery($query);
		$results = $database->loadObjectList();
		$data = $this->formatContentRequest($results, $returnFormat, $callback);
		return $data;
	}
	
	/**
	* Retrieves a set of content items based on their popularity (i.e.) number of hits
	* @param contentLimit - the number of items to return (max is 100)
	* @param contentLimitOffset - used to control the starting point of the query (ex) pagination, get more query, etc
	* @param returnFormat - return format requested (ex) json, xml, php
	* @param callback - the requested JSON callback function name
	*/
	function getContentByPopularity($contentLimit, $contentLimitOffset, $returnFormat, $callback) {
		global $database;
		
		$query = $this->getContentQueryHeader();  //Get content header
		$query .= $this->getPublishFilter(); //Append published filter
		//Add limit, offset, and order by clause
		$query .= "\n ORDER BY hits DESC"
				. "\n LIMIT $contentLimitOffset, $contentLimit";
		$database->setQuery($query);
		$results = $database->loadObjectList();
		$data = $this->formatContentRequest($results, $returnFormat, $callback);
		return $data;
	}
	
	/**
	* Retrieves a set of content items by searching with the user provided query string
	* @param apiQueryString - user provided query string
	* @param contentLimit - the number of items to return (max is 100)
	* @param contentLimitOffset - used to control the starting point of the query (ex) pagination, get more query, etc
	* @param returnFormat - return format requested (ex) json, xml, php
	* @param callback - the requested JSON callback function name
	*/
	function getContentBySearching($apiQueryString, $contentLimit, $contentLimitOffset, $returnFormat, $callback) {
		global $database;
		
		/* Closely mimicing the native MiaCMS site search
			- Chop the query string into words
		 	- Only unique words are searched
			- Must be >2chars to be considered
			- Only the first 25 characters are considered
		*/
		if (strlen($apiQueryString)>25) {
			$apiQueryString = substr($apiQueryString, 0, 25);
		}
		$searchWords = array();
		$queryStrings = explode(' ', $apiQueryString);
		foreach($queryStrings as $queryString) {
			if (strlen($queryString)>2 && !in_array($queryString, $searchWords)) {
				$searchWords[]=$database->getEscaped($queryString);
			}
		}
		
		if (!empty($searchWords)) {
			$query = $this->getContentQueryHeader();  //Get content header
			$query .= $this->getPublishFilter(); //Append published filter
			$query .= "\n AND ("; //Start filter AND clause
			for ($i=0;$i<count($searchWords);$i++) {
				$connector = '';
				if ($i>0) {
					$connector ='OR'; //only added after the first pass
				}
				$query .= "\n {$connector} a.title LIKE '%".$searchWords[$i]."%'"
						. "\n OR a.title_alias LIKE '%".$searchWords[$i]."%'"
						. "\n OR a.introtext LIKE '%".$searchWords[$i]."%'"
						. "\n OR a.fulltext LIKE '%".$searchWords[$i]."%'"
						. "\n OR a.metakey LIKE '%".$searchWords[$i]."%'"
						. "\n OR a.metadesc LIKE '%".$searchWords[$i]."%'";
			}
			$query .= ")"; //end filter AND
			//Add limit, offset, and order by clause
			$query .= "\n LIMIT $contentLimitOffset, $contentLimit";
			$database->setQuery($query);
			$results = $database->loadObjectList();
		} else {
			$results = '';
		}
	
		$data = $this->formatContentRequest($results, $returnFormat, $callback);
		return $data;
	}
	
	/**
	* Retrieves sections (and optionally categories)
	* @param sectionId - specific section id to return (optional)
	* @param showCategories - return categories for each section, yes or no (default is no)
	* @param returnFormat - return format requested (ex) json, xml, php
	* @param callback - the requested JSON callback function name
	*/
	function getSections($sectionId, $showCategories, $returnFormat, $callback) {
		global $database;
		$sectionQuery = "SELECT id, title, name, description, scope, ordering, CONCAT('index.php?option=com_content&task=section&id=', id) AS link"
			   . "\n FROM #__sections"
			   . "\n WHERE published=1 AND access=0";
		if ($sectionId>0) {
			//Specific section passed so just grab that one
			$sectionQuery .= "\n AND id={$sectionId}";
		} else {
			//Not section specific so grab them all and return in order
			$sectionQuery .= "\n ORDER BY ordering ASC";
		}
		$database->setQuery($sectionQuery);
		$dbSections = $database->loadObjectList();
		
		$data = $this->formatSectionRequest($dbSections, $showCategories, $returnFormat, $callback);
		return $data;
	}
	
	/**
	* Builds the standard SELECT SQL header for all type=content retrivals
	*/
	function getContentQueryHeader() {
		$qHeader = "SELECT a.id, a.title, a.title_alias, a.introtext, a.fulltext, a.sectionid"
		         . "\n , s.name AS section, a.catid, cc.name AS category, a.mask, u.name AS author"
				 . "\n , a.created, a.modified, a.publish_up, a.publish_down, a.images, a.urls"
				 . "\n , a.attribs, a.version, a.parentid, a.ordering, a.metakey, a.metadesc, a.hits"
				 . "\n , ROUND(v.rating_sum / v.rating_count) AS rating, v.rating_count"
				 . "\n , CONCAT('index.php?option=com_content&task=view&id=', a.id) AS link"
				 . "\n FROM #__content AS a"
				 . "\n LEFT JOIN #__categories AS cc ON cc.id = a.catid"
				 . "\n LEFT JOIN #__sections AS s ON s.id = a.sectionid"
				 . "\n LEFT JOIN #__users AS u ON u.id = a.created_by"
				 . "\n LEFT JOIN #__content_rating AS v ON a.id = v.content_id";
		
		return $qHeader;
	}
	
	/**
	* Builds the standard published filter check.  We only return public and published content through the API
	*/
	function getPublishFilter() {
		global $mosConfig_offset;
		
		$now = date( "Y-m-d H:i:s", time()+$mosConfig_offset*60*60 );
		$pFilter = "\n WHERE a.state = 1"
				 . "\n AND a.access = 0"
				 . "\n AND ( publish_up = '0000-00-00 00:00:00' OR publish_up <= '$now'  )"
				 . "\n AND ( publish_down = '0000-00-00 00:00:00' OR publish_down >= '$now' )";

		return $pFilter;
	}
	
	/**
	* Process the sections to be returned and build the return layout based on the return type
	* @param sections - array of db section records to be returned with the request
	* @param showCategories - option to return all the categories with each section being returned
	* @param returnFormat - return format requested (ex) json, xml, php
	* @param callback - the requested JSON callback function name
	*/
	function formatSectionRequest($sections, $showCategories, $returnFormat, $callback='') {
		/* If JSON or PHP is the return format and showCategories was requested then
			loop and build the full array before encoding.  If XML we'll loop as we build 
			the XML doc.
		*/
		if ($returnFormat=='json' || $returnFormat=='php')  {
			
			global $database;
            //SEF the links			
		    foreach($sections as $section) {
                $section->link = sefRelToAbs(htmlentities($section->link));
            }
			$sections = $this->object_to_array($sections);
            if ($showCategories=='y') {
				foreach($sections as $key=>$value) {
					$categoryQuery = "SELECT id, parent_id, title, name, description, ordering, CONCAT('index.php?option=com_content&task=category&sectionid=', section, '&id=', id) AS link"
								  . "\n FROM #__categories" 
								  . "\n WHERE published=1"
								  . "\n AND access=0"
								  . "\n AND section={$value['id']}"
								  . "\n ORDER BY ordering ASC";
					$database->setQuery($categoryQuery);
					$dbCategories = $database->loadObjectList();
					//SEF the links	
					if (!empty($dbCategories)) {
						foreach($dbCategories as $dbCategory) {
			                $dbCategory->link = sefRelToAbs(htmlentities($dbCategory->link));
			            }									
						$categories = $this->object_to_array($dbCategories);
						
						$sections[$key]['categories']='';
						if (!empty($categories)) {
							//Add related categories to the sections array
							$sections[$key]['categories'] = $categories;
						}
				    }	
				}
            }
            $data = $this->encodeRestResponse($sections, $returnFormat, $callback);    
		} else if ($returnFormat=='xml') {
		    //For XML we must build the full XML doc
			$dom = new DOMDocument("1.0");
			//Create the root element
			$root = $dom->createElement("Sections");
			$dom->appendChild($root);

			if (count($sections)<1) {
				//No result so just add a single empty node
				$message = $dom->createElement("Section");
				$root->appendChild($message);
			} else {
				//We have results so process accordingly
				foreach($sections as $section) {
					//Create child elements
					$xsection = $dom->createElement("Section");
					$root->appendChild($xsection);
					//Add the id element
					$id = $dom->createElement("id");
					$xsection->appendChild($id);
					$id->appendChild($dom->createTextNode($section->id));
                    //Add the permalink to the section
                    $link = $dom->createElement("link");
                    $xsection->appendChild($link);
                    $link->appendChild($dom->createTextNode(sefRelToAbs(htmlentities($section->link))));					
					//Add the title element
					$title = $dom->createElement("title");
					$xsection->appendChild($title);
					$title->appendChild($dom->createCDATASection($section->title));
					//Add the name element
					$name = $dom->createElement("name");
					$xsection->appendChild($name);
					$name->appendChild($dom->createCDATASection($section->name));
					//Add the description element
					$description = $dom->createElement("description");
					$xsection->appendChild($description);
					$description->appendChild($dom->createCDATASection($section->description));
					//Add the scope element
					$scope = $dom->createElement("scope");
					$xsection->appendChild($scope);
					$scope->appendChild($dom->createTextNode($section->scope));
					//Add the ordering element
					$ordering = $dom->createElement("ordering");
					$xsection->appendChild($ordering);
					$ordering->appendChild($dom->createTextNode($section->ordering));

					//Check for categories if requested
					if ($showCategories=='y') {
						global $database;
						
						$categoryQuery = "SELECT id, parent_id, title, name, description, ordering, CONCAT('index.php?option=com_content&task=category&sectionid=', section, '&id=', id) AS link"
									  . "\n FROM #__categories" 
									  . "\n WHERE published=1"
									  . "\n AND access=0"
									  . "\n AND section={$section->id}"
									  . "\n ORDER BY ordering ASC";
						$database->setQuery($categoryQuery);
						$dbCategories = $database->loadObjectList();

						if (empty($dbCategories)) {
							//No result so just add a single empty node
							$message = $dom->createElement("Categories");
							$root->appendChild($message);
						} else {
							//We have results so process accordingly
							foreach($dbCategories as $dbCategory) {
								//Create child elements
								$categories = $dom->createElement("Categories");
								$xsection->appendChild($categories);
								//Create child elements
								$category = $dom->createElement("Category");
								$categories->appendChild($category);
								//Add the id element
								$id = $dom->createElement("id");
								$category->appendChild($id);
								$id->appendChild($dom->createTextNode($dbCategory->id));
			                    $link = $dom->createElement("link");
			                    $category->appendChild($link);
			                    $link->appendChild($dom->createTextNode(sefRelToAbs(htmlentities($dbCategory->link))));								
								//Add the parent_id element
								$parent_id = $dom->createElement("parent_id");
								$category->appendChild($parent_id);
								$parent_id->appendChild($dom->createTextNode($dbCategory->parent_id));
								//Add the title element
								$title = $dom->createElement("title");
								$category->appendChild($title);
								$title->appendChild($dom->createCDATASection($dbCategory->title));
								//Add the title element
								$name = $dom->createElement("name");
								$category->appendChild($name);
								$name->appendChild($dom->createCDATASection($dbCategory->name));
								//Add the description element
								$description = $dom->createElement("description");
								$category->appendChild($description);
								$description->appendChild($dom->createCDATASection($dbCategory->description));
								//Add the ordering element
								$ordering = $dom->createElement("ordering");
								$category->appendChild($ordering);
								$ordering->appendChild($dom->createTextNode($dbCategory->ordering));
							}
						}
					}
				}
			}
			//Save xml tree
			$data = $dom->saveXML();
		}
		
		return $data;
	}
	
	/**
	* Processes the content to be returned and builds the return layout based on the return type
	* @param dbResults - array of db records to be returned with the request
	* @param returnFormat - return format requested (ex) json, xml, php
	* @param callback - the requested JSON callback function name
	*/
	function formatContentRequest($dbResults, $returnFormat, $callback='') {
		
		if ($returnFormat=='json' || $returnFormat=='php') {
			if (!empty($dbResults)) {
				foreach($dbResults as $dbResult) {
					$dbResult->link = sefRelToAbs(htmlentities($dbResult->link));
				}
			    $data = $this->encodeRestResponse($dbResults, $returnFormat, $callback);
			}
		} else if ($returnFormat=='xml') {
		    //For XML we must build the full XML doc
			$dom = new DOMDocument("1.0");
			//Create the root element
			$root = $dom->createElement("ResultSet");
			$dom->appendChild($root);
			
			if (empty($dbResults)) {
				//No result so just add a single empty node
				$message = $dom->createElement("Result");
				$root->appendChild($message);
			} else {
				//We have results so process accordingly
				foreach($dbResults as $dbResult) {
					//Create child elements
					$content = $dom->createElement("Result");
					$root->appendChild($content);
					//Add the id element
					$id = $dom->createElement("id");
					$content->appendChild($id);
					$id->appendChild($dom->createTextNode($dbResult->id));
                    //Add the permalink to the content item
                    $link = $dom->createElement("link");
                    $content->appendChild($link);
                    $link->appendChild($dom->createTextNode(sefRelToAbs(htmlentities($dbResult->link))));					
					//Add the title element
					$title = $dom->createElement("title");
					$content->appendChild($title);
					$title->appendChild($dom->createCDATASection($dbResult->title));
					//Add the title_alias element
					$title_alias = $dom->createElement("title_alias");
					$content->appendChild($title_alias);
					$title_alias->appendChild($dom->createCDATASection($dbResult->title_alias));
					//Add the introtext element
					$introtext = $dom->createElement("introtext");
					$content->appendChild($introtext);
					$introtext->appendChild($dom->createCDATASection($dbResult->introtext));
					//Add the fulltext element
					$fulltext = $dom->createElement("fulltext");
					$content->appendChild($fulltext);
					$fulltext->appendChild($dom->createCDATASection($dbResult->fulltext));
					//Add the sectionid element
					$sectionid = $dom->createElement("sectionid");
					$content->appendChild($sectionid);
					$sectionid->appendChild($dom->createTextNode($dbResult->sectionid));
					//Add the section element
					$section = $dom->createElement("section");
					$content->appendChild($section);
					$section->appendChild($dom->createTextNode($dbResult->section));
					//Add the catid element
					$catid = $dom->createElement("catid");
					$content->appendChild($catid);
					$catid->appendChild($dom->createTextNode($dbResult->catid));
					//Add the category element
					$category = $dom->createElement("category");
					$content->appendChild($category);
					$category->appendChild($dom->createTextNode($dbResult->category));
					//Add the mask element
					$mask = $dom->createElement("mask");
					$content->appendChild($mask);
					$mask->appendChild($dom->createTextNode($dbResult->mask));
					//Add the author element
					$author = $dom->createElement("author");
					$content->appendChild($author);
					$author->appendChild($dom->createTextNode($dbResult->author));
					//Add the created element
					$created = $dom->createElement("created");
					$content->appendChild($created);
					$created->appendChild($dom->createTextNode($dbResult->created));
					//Add the modified element
					$modified = $dom->createElement("modified");
					$content->appendChild($modified);
					$modified->appendChild($dom->createTextNode($dbResult->modified));
					//Add the publish_up element
					$publish_up = $dom->createElement("publish_up");
					$content->appendChild($publish_up);
					$publish_up->appendChild($dom->createTextNode($dbResult->publish_up));
					//Add the publish_down element
					$publish_down = $dom->createElement("publish_down");
					$content->appendChild($publish_down);
					$publish_down->appendChild($dom->createTextNode($dbResult->publish_down));
					//Add the images element
					$images = $dom->createElement("images");
					$content->appendChild($images);
					$images->appendChild($dom->createTextNode($dbResult->images));
					//Add the urls element
					$urls = $dom->createElement("urls");
					$content->appendChild($urls);
					$urls->appendChild($dom->createCDATASection($dbResult->urls));
					//Add the attribs element
					$attribs = $dom->createElement("attribs");
					$content->appendChild($attribs);
					$attribs->appendChild($dom->createCDATASection($dbResult->attribs));
					//Add the version element
					$version = $dom->createElement("version");
					$content->appendChild($version);
					$version->appendChild($dom->createTextNode($dbResult->version));
					//Add the parentid element
					$parentid = $dom->createElement("parentid");
					$content->appendChild($parentid);
					$parentid->appendChild($dom->createTextNode($dbResult->parentid));
					//Add the ordering element
					$ordering = $dom->createElement("ordering");
					$content->appendChild($ordering);
					$ordering->appendChild($dom->createTextNode($dbResult->ordering));
					//Add the metakey element
					$metakey = $dom->createElement("metakey");
					$content->appendChild($metakey);
					$metakey->appendChild($dom->createCDATASection($dbResult->metakey));
					//Add the metadesc element
					$metadesc = $dom->createElement("metadesc");
					$content->appendChild($metadesc);
					$metadesc->appendChild($dom->createCDATASection($dbResult->metadesc));
					//Add the hits element
					$hits = $dom->createElement("hits");
					$content->appendChild($hits);
					$hits->appendChild($dom->createTextNode($dbResult->hits));
					//Add the rating element
					$rating = $dom->createElement("rating");
					$content->appendChild($rating);
					$rating->appendChild($dom->createTextNode($dbResult->rating));
					//Add the rating_count element
					$rating_count = $dom->createElement("rating_count");
					$content->appendChild($rating_count);
					$rating_count->appendChild($dom->createTextNode($dbResult->rating_count));
				}
			}
			//Save xml tree
			$data = $dom->saveXML();
		} 
		
		return $data;
	}
	
}

?>